#pragma once
#include "vec3.h"
#include "GlobalDefine.h"
#include <cmath>

namespace mray{
	struct Result;
	struct HemisphereSampler;

	struct Atten{

		Atten(){
			m_linear = 0.6f;
			m_constant = 0.2f;
			m_exp = 0.2f;
		}

		Atten(float constant, float linear, float exp){
			m_constant = constant;
			m_linear = linear;
			m_exp = exp;
		}

		Atten(const Atten& a){
			m_linear = a.m_linear;
			m_exp = a.m_exp;
			m_constant = a.m_constant;
		}

		Atten& operator = (const Atten& a){
			m_linear = a.m_linear;
			m_exp = a.m_exp;
			m_constant = a.m_constant;
		}

		float CalcAtten(float r)const{
			float inv_r = 1.0f/r;
			return m_constant + m_exp*inv_r* inv_r + m_linear*inv_r;
		}

		float m_linear;
		float m_exp;
		float m_constant;
	};

	struct BaseLight{
		
		BaseLight(const vec3& color, bool castShadow):m_color(color), m_castShadow(castShadow){	}
		virtual ~BaseLight(){}

		virtual vec3 Illuminate(const Result& result)const = 0;
	protected:
		vec3 m_color;
		bool m_castShadow;
	};

	struct AmbientLight : public BaseLight{
		AmbientLight(const vec3& color, int sub);
		virtual ~AmbientLight();
		virtual vec3 Illuminate(const Result& result)const;

	protected:
		HemisphereSampler* m_sampler;
	};

	struct PointLight : public BaseLight{
		PointLight(const vec3& color, const vec3& _position, bool castShadow) :
			BaseLight(color, castShadow), m_pos(_position){ }
		PointLight(const vec3& color, const vec3& pos, const Atten& atten, bool castShadow) :
			BaseLight(color, castShadow), m_pos(pos), m_atten(atten){ }
		~PointLight(){}

		virtual vec3 Illuminate(const Result& result)const;
	private:
		vec3 m_pos;
		Atten m_atten;
	};

	struct DirectionalLight : public BaseLight{
		DirectionalLight(const vec3& color, const vec3& dir, bool castShadow);
		virtual ~DirectionalLight(){}

		virtual vec3 Illuminate(const Result& result)const;
	private:
		//it inverse the orientation of light for easily computation
		vec3 m_dir;
	};

}